]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
st/scroll-bar: Port scroll bar scrolling to use ClutterGesture
authorJonas Dreßler <verdre@v0yd.nl>
Sat, 6 Aug 2022 12:46:50 +0000 (14:46 +0200)
committerCarlos Garnacho <carlosg@gnome.org>
Thu, 28 Aug 2025 17:14:06 +0000 (19:14 +0200)
Avoid the troublesome event handling here and replace it with two
ClutterPanGestures, one for the trough and one for the handle.

This is a fairly non-intrusive port, there's room for a lot of
improvements, for example making the trough react to motion events too
should be fairly easy now.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2853>

src/st/st-scroll-bar.c

index 32dbcd7e39d2cbf9602e2e2625daa702ba3ab0f2..056f9870c24ba7b2344a394cc3bcc69281dac106 100644 (file)
@@ -52,15 +52,15 @@ struct _StScrollBarPrivate
   gfloat        x_origin;
   gfloat        y_origin;
 
-  ClutterInputDevice *grab_device;
-  ClutterGrab *grab;
-
   ClutterActor *trough;
   ClutterActor *handle;
 
   gfloat        move_x;
   gfloat        move_y;
 
+  ClutterPanGesture *trough_pan_gesture;
+  ClutterPanGesture *handle_pan_gesture;
+
   /* Trough-click handling. */
   enum { NONE, UP, DOWN }  paging_direction;
   guint             paging_source_id;
@@ -95,13 +95,6 @@ enum
 
 static guint signals[LAST_SIGNAL] = { 0, };
 
-static gboolean
-handle_button_press_event_cb (ClutterActor *actor,
-                              ClutterEvent *event,
-                              StScrollBar  *bar);
-
-static void stop_scrolling (StScrollBar *bar);
-
 ClutterOrientation
 st_scroll_bar_get_orientation (StScrollBar *bar)
 {
@@ -212,9 +205,12 @@ st_scroll_bar_dispose (GObject *gobject)
 static void
 st_scroll_bar_unmap (ClutterActor *actor)
 {
-  CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->unmap (actor);
+  StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (ST_SCROLL_BAR (actor));
+
+  if (priv->handle)
+    clutter_gesture_cancel (CLUTTER_GESTURE (priv->handle_pan_gesture));
 
-  stop_scrolling (ST_SCROLL_BAR (actor));
+  CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->unmap (actor);
 }
 
 static void
@@ -647,91 +643,6 @@ move_slider (StScrollBar *bar,
   st_adjustment_set_value (priv->adjustment, position);
 }
 
-static void
-stop_scrolling (StScrollBar *bar)
-{
-  StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
-  if (!priv->grab_device)
-    return;
-
-  st_widget_remove_style_pseudo_class (ST_WIDGET (priv->handle), "active");
-
-  if (priv->grab)
-    {
-      clutter_grab_dismiss (priv->grab);
-      g_clear_object (&priv->grab);
-    }
-
-  priv->grab_device = NULL;
-  g_signal_emit (bar, signals[SCROLL_STOP], 0);
-}
-
-static gboolean
-handle_motion_event_cb (ClutterActor *trough,
-                        ClutterEvent *event,
-                        StScrollBar  *bar)
-{
-  StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
-  graphene_point_t coords;
-
-  if (!priv->grab_device)
-    return FALSE;
-
-  clutter_event_get_position (event, &coords);
-  move_slider (bar, coords.x, coords.y);
-  return TRUE;
-}
-
-static gboolean
-handle_button_release_event_cb (ClutterActor *trough,
-                                ClutterEvent *event,
-                                StScrollBar  *bar)
-{
-  if (clutter_event_get_button (event) != 1)
-    return FALSE;
-
-  stop_scrolling (bar);
-  return TRUE;
-}
-
-static gboolean
-handle_button_press_event_cb (ClutterActor *actor,
-                              ClutterEvent *event,
-                              StScrollBar  *bar)
-{
-  StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (bar);
-  ClutterInputDevice *device = clutter_event_get_device (event);
-  graphene_point_t coords;
-  ClutterActor *stage;
-
-  if (clutter_event_get_button (event) != 1)
-    return FALSE;
-
-  clutter_event_get_position (event, &coords);
-
-  if (!clutter_actor_transform_stage_point (priv->handle,
-                                            coords.x,
-                                            coords.y,
-                                            &priv->x_origin,
-                                            &priv->y_origin))
-    return FALSE;
-
-  st_widget_add_style_pseudo_class (ST_WIDGET (priv->handle), "active");
-
-  /* Account for the scrollbar-trough-handle nesting. */
-  priv->x_origin += clutter_actor_get_x (priv->trough);
-  priv->y_origin += clutter_actor_get_y (priv->trough);
-
-  g_assert (!priv->grab_device);
-
-  stage = clutter_actor_get_stage (actor);
-  priv->grab = clutter_stage_grab (CLUTTER_STAGE (stage), priv->handle);
-  priv->grab_device = device;
-  g_signal_emit (bar, signals[SCROLL_START], 0);
-
-  return TRUE;
-}
-
 static gboolean
 trough_paging_cb (StScrollBar *self)
 {
@@ -855,63 +766,94 @@ trough_paging_cb (StScrollBar *self)
   return ret;
 }
 
-static gboolean
-trough_button_press_event_cb (ClutterActor *actor,
-                              ClutterEvent *event,
-                              StScrollBar  *self)
+static void
+trough_pan_recognize_cb (ClutterPanGesture *pan_gesture,
+                         StScrollBar       *self)
 {
-  StScrollBarPrivate *priv;
-  graphene_point_t coords;
-
-  g_return_val_if_fail (self, FALSE);
-
-  if (clutter_event_get_button (event) != 1)
-    return FALSE;
+  StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (self);
+  graphene_point_t centroid;
 
-  priv = st_scroll_bar_get_instance_private (self);
-  if (priv->adjustment == NULL)
-    return FALSE;
+  if (!priv->adjustment)
+    return;
 
-  clutter_event_get_position (event, &coords);
+  clutter_pan_gesture_get_centroid_abs (pan_gesture, &centroid);
 
-  priv->move_x = coords.x;
-  priv->move_y = coords.y;
+  priv->move_x = centroid.x;
+  priv->move_y = centroid.y;
   priv->paging_direction = NONE;
   priv->paging_event_no = 0;
   trough_paging_cb (self);
-
-  return TRUE;
 }
 
-static gboolean
-trough_button_release_event_cb (ClutterActor *actor,
-                                ClutterEvent *event,
-                                StScrollBar  *self)
+static void
+trough_pan_end_cb (ClutterPanGesture *pan_gesture,
+                   StScrollBar       *self)
 {
   StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (self);
 
-  if (clutter_event_get_button (event) != 1)
-    return FALSE;
+  g_clear_handle_id (&priv->paging_source_id, g_source_remove);
+}
+
+static void
+trough_pan_cancel_cb (ClutterPanGesture *pan_gesture,
+                      StScrollBar       *self)
+{
+  StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (self);
 
   g_clear_handle_id (&priv->paging_source_id, g_source_remove);
+}
 
-  return TRUE;
+static void
+handle_pan_recognize_cb (ClutterPanGesture *pan_gesture,
+                         StScrollBar       *self)
+{
+  StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (self);
+  graphene_point_t centroid;
+
+  clutter_pan_gesture_get_centroid_abs (pan_gesture, &centroid);
+  if (!clutter_actor_transform_stage_point (priv->handle, centroid.x, centroid.y, &centroid.x, &centroid.y))
+    return;
+
+  priv->x_origin = centroid.x;
+  priv->y_origin = centroid.y;
+
+  st_widget_add_style_pseudo_class (ST_WIDGET (priv->handle), "active");
+
+  /* Account for the scrollbar-trough-handle nesting. */
+  priv->x_origin += clutter_actor_get_x (priv->trough);
+  priv->y_origin += clutter_actor_get_y (priv->trough);
+
+  g_signal_emit (self, signals[SCROLL_START], 0);
 }
 
-static gboolean
-trough_leave_event_cb (ClutterActor *actor,
-                       ClutterEvent *event,
-                       StScrollBar  *self)
+static void
+handle_pan_update_cb (ClutterPanGesture *pan_gesture,
+                      StScrollBar       *self)
+{
+  graphene_point_t centroid;
+
+  clutter_pan_gesture_get_centroid_abs (pan_gesture, &centroid);
+  move_slider (self, centroid.x, centroid.y);
+}
+
+static void
+handle_pan_end_cb (ClutterPanGesture *pan_gesture,
+                   StScrollBar       *self)
 {
   StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (self);
 
-  if (priv->paging_source_id)
-    {
-      g_clear_handle_id (&priv->paging_source_id, g_source_remove);
-      return TRUE;
-    }
+  st_widget_remove_style_pseudo_class (ST_WIDGET (priv->handle), "active");
+  g_signal_emit (self, signals[SCROLL_STOP], 0);
+}
 
-  return FALSE;
+static void
+handle_pan_cancel_cb (ClutterPanGesture *pan_gesture,
+                      StScrollBar       *self)
+{
+  StScrollBarPrivate *priv = st_scroll_bar_get_instance_private (self);
+
+  st_widget_remove_style_pseudo_class (ST_WIDGET (priv->handle), "active");
+  g_signal_emit (self, signals[SCROLL_STOP], 0);
 }
 
 static void
@@ -935,12 +877,20 @@ st_scroll_bar_init (StScrollBar *self)
   clutter_actor_set_name (CLUTTER_ACTOR (priv->trough), "trough");
   clutter_actor_add_child (CLUTTER_ACTOR (self),
                            CLUTTER_ACTOR (priv->trough));
-  g_signal_connect (priv->trough, "button-press-event",
-                    G_CALLBACK (trough_button_press_event_cb), self);
-  g_signal_connect (priv->trough, "button-release-event",
-                    G_CALLBACK (trough_button_release_event_cb), self);
-  g_signal_connect (priv->trough, "leave-event",
-                    G_CALLBACK (trough_leave_event_cb), self);
+
+  priv->trough_pan_gesture = CLUTTER_PAN_GESTURE (clutter_pan_gesture_new ());
+  clutter_pan_gesture_set_begin_threshold (priv->trough_pan_gesture, 0);
+  clutter_actor_meta_set_name (CLUTTER_ACTOR_META (priv->trough_pan_gesture),
+                               "StScrollBar trough pan");
+
+  g_signal_connect (priv->trough_pan_gesture, "recognize",
+                    G_CALLBACK (trough_pan_recognize_cb), self);
+  g_signal_connect (priv->trough_pan_gesture, "end",
+                    G_CALLBACK (trough_pan_end_cb), self);
+  g_signal_connect (priv->trough_pan_gesture, "cancel",
+                    G_CALLBACK (trough_pan_cancel_cb), self);
+
+  clutter_actor_add_action (CLUTTER_ACTOR (priv->trough), CLUTTER_ACTION (priv->trough_pan_gesture));
 
   priv->handle = (ClutterActor *) g_object_new (ST_TYPE_WIDGET, NULL);
   st_widget_set_track_hover (ST_WIDGET (priv->handle), TRUE);
@@ -948,12 +898,22 @@ st_scroll_bar_init (StScrollBar *self)
   st_widget_set_style_class_name (ST_WIDGET (priv->handle), "hhandle");
   clutter_actor_add_child (CLUTTER_ACTOR (self),
                            CLUTTER_ACTOR (priv->handle));
-  g_signal_connect (priv->handle, "button-press-event",
-                    G_CALLBACK (handle_button_press_event_cb), self);
-  g_signal_connect (priv->handle, "button-release-event",
-                    G_CALLBACK (handle_button_release_event_cb), self);
-  g_signal_connect (priv->handle, "motion-event",
-                    G_CALLBACK (handle_motion_event_cb), self);
+
+  priv->handle_pan_gesture = CLUTTER_PAN_GESTURE (clutter_pan_gesture_new ());
+  clutter_pan_gesture_set_begin_threshold (priv->handle_pan_gesture, 0);
+  clutter_actor_meta_set_name (CLUTTER_ACTOR_META (priv->handle_pan_gesture),
+                               "StScrollBar handle pan");
+
+  g_signal_connect (priv->handle_pan_gesture, "recognize",
+                    G_CALLBACK (handle_pan_recognize_cb), self);
+  g_signal_connect (priv->handle_pan_gesture, "pan-update",
+                    G_CALLBACK (handle_pan_update_cb), self);
+  g_signal_connect (priv->handle_pan_gesture, "end",
+                    G_CALLBACK (handle_pan_end_cb), self);
+  g_signal_connect (priv->handle_pan_gesture, "cancel",
+                    G_CALLBACK (handle_pan_cancel_cb), self);
+
+  clutter_actor_add_action (CLUTTER_ACTOR (priv->handle), CLUTTER_ACTION (priv->handle_pan_gesture));
 
   clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);